#pragma once
#include <type_traits>

namespace cpp {

	template <class T = void>
	struct identity
	{
		typedef T type;

		template<class U>
		U&& operator()(U&& arg) const
		{
			return std::forward<U>(arg);
		}
	};

	template <>
	struct identity<void>
	{
		template<class U> U&& operator()(U&& arg) const
		{
			return std::forward<U>(arg);
		}
		typedef std::true_type is_transparent;
		typedef void type;
	};

	template <class T>
	using identity_t = typename identity<T>::type;

	//////////////////////////////////////////////////////////////////////////

	//template <class T, class U  >
	//using forward_as = std::enable_if_t<std::is_same<U, std::remove_cv_t<  std::remove_reference_t<T>> >::value >;

	//#a Concept resolution
	//template<typename T, typename U> concept bool Forward()
	//{ return std::is_same<U, std::remove_cv_t<  std::remove_reference_t<T>> >::value; }

	//void foo(Forward<int>&& i){ std::forward<decltype(i)>(i); };

	namespace __type_traits
	{
		template<class T, bool = std::is_enum<T>::value>
		struct _Underlying_type {};
		template<class T>
		struct _Underlying_type<T, true> { using type = std::underlying_type_t<T>; };
	}

	template<class T>
	struct underlying_type : __type_traits::_Underlying_type<T> { };

	template<class T>
	using underlying_type_t = typename underlying_type<T>::type;


	//////////////////////////////////////////////////////////////////////////

	template<class...> struct conjunction : std::true_type { };
	template<class B1> struct conjunction<B1> : B1 { };
	template<class B1, class... Bn>
	struct conjunction<B1, Bn...> : std::conditional_t<B1::value != false, conjunction<Bn...>, B1> {};


	template<class...> struct disjunction : std::false_type { };
	template<class B1> struct disjunction<B1> : B1 { };
	template<class B1, class... Bn>
	struct disjunction<B1, Bn...> : std::conditional_t<B1::value != false, B1, disjunction<Bn...>> { };

	template<class B>
	struct negation : std::bool_constant<!B::value> { };


	//////////////////////////////////////////////////////////////////////////
	struct nonesuch
	{
		nonesuch() = delete;
		~nonesuch() = delete;
		nonesuch(nonesuch const&) = delete;
		void operator=(nonesuch const&) = delete;
	};

	namespace __type_traits
	{
		template <class Default, class AlwaysVoid,
			template<class...> class Op, class... Args>
		struct detector {
			using value_t = std::false_type;
			using type = Default;
		};

		template <class Default, template<class...> class Op, class... Args>
		struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> {
			using value_t = std::true_type;
			using type = Op<Args...>;
		};

	} // namespace detail

	template <template<class...> class Op, class... Args>
	using is_detected = typename __type_traits::detector<nonesuch, void, Op, Args...>::value_t;

	template <template<class...> class Op, class... Args>
	using detected_t = typename __type_traits::detector<nonesuch, void, Op, Args...>::type;

	template <class Default, template<class...> class Op, class... Args>
	using detected_or = __type_traits::detector<Default, void, Op, Args...>;

	template< class Default, template<class...> class Op, class... Args >
	using detected_or_t = typename detected_or<Default, Op, Args...>::type;

#if __cpp_variable_templates

#if __cpp_inline_variables
	template< template<class...> class Op, class... Args >
	inline constexpr bool is_detected_v = is_detected<Op, Args...>::value;
#else
	template< template<class...> class Op, class... Args >
	constexpr bool is_detected_v = is_detected<Op, Args...>::value;
#endif

#endif


	template <class Expected, template<class...> class Op, class... Args>
	using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;

	template <class Expected, template<class...> class Op, class... Args>
	inline constexpr bool is_detected_exact_v = is_detected_exact<Expected, Op, Args...>::value;

	template <class To, template<class...> class Op, class... Args>
	using is_detected_convertible = std::is_convertible<detected_t<Op, Args...>, To>;

	template <class To, template<class...> class Op, class... Args>
	inline constexpr bool is_detected_convertible_v = is_detected_convertible<To, Op, Args...>::value;


	namespace __type_traits
	{
		using std::swap;

		template<class _Ty1, class _Ty2, class = void>
		struct _Swappable_with_helper : std::false_type
		{
		};

		struct _Swappable_with_helper_unique_type {}; // TRANSITION, C1XX

		template<class _Ty1, class _Ty2>
		struct _Swappable_with_helper<_Ty1, _Ty2, std::void_t<
			_Swappable_with_helper_unique_type,
			decltype(swap(std::declval<_Ty1>(), std::declval<_Ty2>()))> >
			:std::true_type
		{
		};

		// CLASS TEMPLATE _Is_swappable_with
		template<class _Ty1, class _Ty2>
		struct _Is_swappable_with
			: conjunction<
			_Swappable_with_helper<_Ty1, _Ty2>,
			_Swappable_with_helper<_Ty2, _Ty1>>::type
		{
		};

		template<class _Ty>
		struct _Is_swappable
			: _Is_swappable_with<
			typename std::add_lvalue_reference<_Ty>::type,
			typename std::add_lvalue_reference<_Ty>::type>::type
		{
		};

		// CLASS TEMPLATE _Swap_cannot_throw
		template<class _Ty1,
			class _Ty2>
			struct _Swap_cannot_throw
		{	// Determine if expressions with type and value category _Ty1 and _Ty2
			// (presumed to satisfy is_swappable_with) can be swapped without
			// emitting exceptions
			static constexpr bool value = // TRANSITION, VSO#207029
				noexcept(swap(std::declval<_Ty1>(), std::declval<_Ty2>()))
				&& noexcept(swap(std::declval<_Ty2>(), std::declval<_Ty1>()));

			using type = std::bool_constant<value>;
		};


		template<class _Ty1, class _Ty2>
		struct _Is_nothrow_swappable_with
			: conjunction<_Is_swappable_with<_Ty1, _Ty2>,
			_Swap_cannot_throw<_Ty1, _Ty2>>::type
		{
		};


		template<class _Ty>
		struct _Is_nothrow_swappable : _Is_nothrow_swappable_with<
			typename std::add_lvalue_reference<_Ty>::type,
			typename std::add_lvalue_reference<_Ty>::type>::type
		{
		};
	}


	template<class _Ty1, class _Ty2>
	struct is_swappable_with : __type_traits::_Is_swappable_with<_Ty1, _Ty2>::type {};

	template<class _Ty>
	struct is_swappable : __type_traits::_Is_swappable<_Ty>::type {	};

	template<class _Ty1, class _Ty2>
	struct is_nothrow_swappable_with :__type_traits::_Is_nothrow_swappable_with<_Ty1, _Ty2>::type {};

	template<class _Ty>
	struct is_nothrow_swappable :__type_traits::_Is_nothrow_swappable<_Ty>::type {};

	template<class _Ty1, class _Ty2>
	constexpr bool is_swappable_with_v = is_swappable_with<_Ty1, _Ty2>::value;

	template<class _Ty>
	constexpr bool is_swappable_v = is_swappable<_Ty>::value;

	template<class _Ty1, class _Ty2>
	constexpr bool is_nothrow_swappable_with_v = is_nothrow_swappable_with<_Ty1, _Ty2>::value;

	template<class _Ty>
	constexpr bool is_nothrow_swappable_v = is_nothrow_swappable<_Ty>::value;

	//////////////////////////////////////////////////////////////////////////

	template <size_t N, typename... T>
	struct pack_element;
	template <size_t N, typename... T>
	using  pack_element_t = typename pack_element<N, T...>::type;

	template<class ...T>
	struct pack;

	namespace __type_traits {

		template<class T, class U>
		struct _PackAppend;

		template<class... T, class ...U>
		struct _PackAppend< pack<T...>, pack<U...> >
		{
			using type = pack<  T..., U...>;
		};

		template<size_t b, size_t e, class ...T>
		struct _PackHelper
		{
			static_assert(b <= e, "error range: need ( b <= e ) ");
			using type = typename _PackAppend <
				pack < pack_element_t<b, T...> >,
				typename _PackHelper< b + 1, e, T...>::type     > ::type;
		};

		template<size_t e, class ...T>
		struct _PackHelper<e, e, T...>
		{
			using type = pack<>;
		};
	}

	template<class ...T>
	struct pack
	{
		template<size_t b, size_t e>
		using sub_pack = typename __type_traits::_PackHelper<b, e, T...>::type;
	};

	template<class Pack, size_t b, size_t e>
	using sub_pack = typename Pack::template sub_pack<b, e >;


	template <typename T0, typename... T>
	struct pack_element<0, T0, T...> {
		typedef T0 type;
	};
	template <size_t N, typename T0, typename... T>
	struct pack_element<N, T0, T...> {
		typedef typename pack_element<N - 1, T...>::type type;
	};


	template <typename T0, typename... T>
	struct pack_element<0, pack< T0, T...> > {
		typedef T0 type;
	};
	template <size_t N, typename T0, typename... T>
	struct pack_element<N, pack< T0, T...> > {
		typedef typename pack_element<N - 1, pack< T...> >::type type;
	};


	//////////////////////////////////////////////////////////////////////////
	namespace __type_traits {

		template<class C>
		struct ClassType
		{
			using class_type = C;
		};
		template<>
		struct ClassType<void>
		{
		};

		enum :size_t
		{
			FreeFunction = 1,
			MemberFunction,
			FunctionObject
		};

		template<size_t index, class C = void>
		struct CallableType :ClassType<C>
		{
			using is_free_function = std::bool_constant<index == FreeFunction>;
			using is_member_function = std::bool_constant<index == MemberFunction>;
			using is_function_object = std::bool_constant<index == FunctionObject>;
		};


		template<bool isConst, bool isVolatile, bool isRef, bool isLRef, bool isNoexcept = false>
		struct QualificationTraits
		{
			using is_const = std::bool_constant<isConst>;
			using is_volatile = std::bool_constant<isVolatile>;

			using is_ref = std::bool_constant<isRef>;
			using is_lref = std::bool_constant<isRef&& isLRef>;
			using is_rref = std::bool_constant<isRef && !isLRef>;

			using is_noexcept = std::bool_constant<isNoexcept>;
		};

		template<class Fn, class C, class = void>
		struct ArgumentsTraits;


		template<class R, class ... Args, class _CallableType>
		struct ArgumentsTraits<R(Args...), _CallableType > : _CallableType
		{
			template<size_t index>
			using arg_type_t = typename pack_element<index, Args...>::type;
			static const size_t arity = sizeof...(Args);
			using result_type = R;
			using is_variadic = std::bool_constant<false>;
		};

		template< class R, class ... Args, class _CallableType>
		struct ArgumentsTraits<R(Args..., ...), _CallableType > : _CallableType
		{
			template<size_t index>
			using arg_type_t = typename pack_element<index, Args...>::type;
			static const size_t arity = sizeof...(Args);
			using result_type = R;
			using is_variadic = std::bool_constant<true>;
		};



		template<class Fn, class C = void, class = void>
		struct FunctionTraits;

	#if !defined (_WIN64)

	#define __CALL __cdecl
	#define VARIADIC
	#define __NOEXCEPT 
	#define __ISNOEXCEPT false
	#include "impls/call_traits_template.ipp"
	#undef __ISNOEXCEPT
	#undef __NOEXCEPT
	#undef VARIADIC
	#undef __CALL

	#define __CALL __stdcall
	#define __NOEXCEPT 
	#define __ISNOEXCEPT false
	#include "impls/call_traits_template.ipp"
	#undef __ISNOEXCEPT
	#undef __NOEXCEPT
	#undef __CALL

	#define __CALL __fastcall
	#define __NOEXCEPT 
	#define __ISNOEXCEPT false
	#include "impls/call_traits_template.ipp"
	#undef __ISNOEXCEPT
	#undef __NOEXCEPT
	#undef __CALL
	#else

	#define __CALL 
	#define VARIADIC
	#define __NOEXCEPT 
	#define __ISNOEXCEPT false
	#include "impls/call_traits_template.ipp"
	#undef __ISNOEXCEPT
	#undef __NOEXCEPT
	#undef VARIADIC
	#undef __CALL

	#endif

	#define __CALL __vectorcall
	#define __NOEXCEPT 
	#define __ISNOEXCEPT false
	#include "impls/call_traits_template.ipp"
	#undef __ISNOEXCEPT
	#undef __NOEXCEPT
	#undef __CALL

		//////////////////////////////////////////////////////////////////////////

	#if 0
	#define __CALL __cdecl
	#define VARIADIC
	#define __NOEXCEPT noexcept
	#define __ISNOEXCEPT true
	#include "impls/call_traits_template.ipp"
	#undef __ISNOEXCEPT
	#undef __NOEXCEPT
	#undef VARIADIC
	#undef __CALL

	#define __CALL __stdcall
	#define __NOEXCEPT noexcept
	#define __ISNOEXCEPT true
	#include "impls/call_traits_template.ipp"
	#undef __ISNOEXCEPT
	#undef __NOEXCEPT
	#undef __CALL

	#define __CALL __fastcall
	#define __NOEXCEPT noexcept
	#define __ISNOEXCEPT true
	#include "impls/call_traits_template.ipp"
	#undef __ISNOEXCEPT
	#undef __NOEXCEPT
	#undef __CALL

	#define __CALL __vectorcall
	#define __NOEXCEPT noexcept
	#define __ISNOEXCEPT true
	#include "impls/call_traits_template.ipp"
	#undef __ISNOEXCEPT
	#undef __NOEXCEPT
	#undef __CALL
	#endif
			/************************************************************************/
			/*                                                                      */
			/************************************************************************/


		template<class Fn, int = 0, class = void>
		struct function_traits
		{
		};

		template<class Sign, int tag >
		struct function_traits< Sign, tag, std::void_t< std::enable_if_t<std::is_function<Sign>::value >>>
			:FunctionTraits<Sign, CallableType<FreeFunction > >
		{
		};

		//////////////////////////////////////////////////////////////////////////
		template<class Sign, int tag>
		struct function_traits< Sign*, tag, std::void_t< std::enable_if_t<std::is_function<Sign>::value >> >
			:FunctionTraits<Sign, CallableType<FreeFunction > >
		{
		};


		//////////////////////////////////////////////////////////////////////////
		template<class Sign, class C, int tag>
		struct function_traits<Sign C::*, tag> : FunctionTraits< Sign, CallableType<MemberFunction, C > >
		{
		};

		template<class Sign, class C >
		struct function_traits<Sign C::*, 1> : FunctionTraits< Sign, CallableType<FunctionObject, C > >
		{
		};

		template<class Functor>
		struct function_traits<Functor, 0, std::void_t<
			std::enable_if_t< std::is_class<Functor>::value > >
		> :function_traits< decltype(&Functor::operator()), 1 >
		{

		};
	}

	template<class _Callable>
	struct callable_traits : __type_traits::function_traits<std::decay_t< _Callable> >
	{

	};

}


